package com.css.spring.formework.context;

import com.css.spring.formework.annotation.Autowired;
import com.css.spring.formework.annotation.Controller;
import com.css.spring.formework.annotation.Service;
import com.css.spring.formework.aop.AopConfig;
import com.css.spring.formework.beans.BeanDefinition;
import com.css.spring.formework.beans.BeanPostProcessor;
import com.css.spring.formework.beans.BeanWrapper;
import com.css.spring.formework.context.support.BeanDefinitionReader;
import com.css.spring.formework.core.BeanFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ApplicationContext extends DefaultListableBeanFactory implements BeanFactory {

    private String[] configLocations;
    private BeanDefinitionReader reader;
    //css//用来包装注册时单例的容器
    private Map<String, Object> beanCacheMap = new HashMap<String, Object>();
    //css//存储所有的被代理过的对象
    private Map<String, BeanWrapper> beanWrapperMap = new ConcurrentHashMap<String, BeanWrapper>();

    public ApplicationContext(String... locations) {
        configLocations = locations;
        refresh();
    }

    public void refresh() {

        this.reader = new BeanDefinitionReader(configLocations);

        //css//定位
        //css//加载
        List<String> beanDefinitions = this.reader.loadBeanDefinition();
        //css//注册
        doRegistry(beanDefinitions);
        //css//依赖注入
        doAutoWired();
        System.out.println(1);

    }

    //css//开始执行自动化的依赖注入
    private void doAutoWired() {
        Set<Map.Entry<String, BeanDefinition>> entries = this.beanDefinitionMap.entrySet();
        for (Map.Entry<String, BeanDefinition> entry : entries) {
            String className = entry.getKey();
            if (!entry.getValue().isLazyInit()) {
                getBean(className);
            }
        }

    }

    private void doRegistry(List<String> beanDefinitions) {
        try {
            for (String beanDefinitionName : beanDefinitions) {

                Class<?> aClass = Class.forName(beanDefinitionName);
                if (aClass.isInterface()) {
                    continue;
                }

                BeanDefinition beanDefinition = this.reader.registryBean(beanDefinitionName);
                if (beanDefinition != null) {
                    this.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
                }
                Class<?>[] interfaces = aClass.getInterfaces();
                for (Class<?> anInterface : interfaces) {
                    //css//如果是多个实现类只能覆盖
                    this.beanDefinitionMap.put(anInterface.getName(), beanDefinition);
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 从ioc容器中获取 beanDefinition
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {

        BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        try {
            //css//生成通知时间
            BeanPostProcessor postProcessor = new BeanPostProcessor();
            //css//实例化
            Object instance = instantionBean(beanDefinition);
            if (instance == null) {
                return null;
            }
            postProcessor.postProcessBeforeInitialization(instance, beanName);

            BeanWrapper beanWrapper = new BeanWrapper(instance);

            beanWrapper.setAopConfig(instantionAopConfig(beanDefinition));

            beanWrapper.setPostProcessor(postProcessor);

            beanWrapperMap.put(beanName, beanWrapper);

            postProcessor.postProcessAfterInitialization(instance, beanName);

            populateBean(beanName, instance);

            return this.beanWrapperMap.get(beanName).getWrapperInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public void populateBean(String beanName, Object instance) throws IllegalAccessException {
        Class<?> aClass = instance.getClass();
        if (!aClass.isAnnotationPresent(Controller.class) && !aClass.isAnnotationPresent(Service.class)) {
            return;
        }
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(Autowired.class)) {
                continue;
            }
            field.setAccessible(true);
            if (field.get(instance) != null) {
                continue;
            }

            Autowired annotation = field.getAnnotation(Autowired.class);
            String value = annotation.value().trim();
            if ("".equals(value)) {
                value = field.getType().getName();
            }
            try {
                BeanWrapper beanWrapper = this.beanWrapperMap.get(value);

                if (beanWrapper == null) {
                    Object bean = getBean(value);
                    field.set(instance, bean);
                } else {
                    field.set(instance, beanWrapper.getWrapperInstance());
                }
//                System.out.println(beanWrapper);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private AopConfig instantionAopConfig(BeanDefinition beanDefinition) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException {
        AopConfig aopConfig = new AopConfig();

        String pointCut = reader.getConfig().getProperty("pointCut");
        String[] aspectBefore = reader.getConfig().getProperty("aspectBefore").split("\\s");
        String[] aspectAfter = reader.getConfig().getProperty("aspectAfter").split("\\s");

        Class<?> aClass = Class.forName(beanDefinition.getBeanClassName());
        Class<?> aspBeforeClass = Class.forName(aspectBefore[0]);


        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {

            Pattern pattern = Pattern.compile(pointCut);

            Matcher matcher = pattern.matcher(declaredMethod.toString());

            if (matcher.matches()) {
                Method method = aspBeforeClass.getMethod(aspectBefore[1]);

                aopConfig.put(declaredMethod,
                        aspBeforeClass.newInstance(),
                        new Method[]{
                                method,
                                aspBeforeClass.getMethod(aspectAfter[1])});
            }
        }
        return aopConfig;
    }

    private Object instantionBean(BeanDefinition beanDefinition) {
        Object instance = null;
        String className = beanDefinition.getBeanClassName();
        try {

            Class<?> aClass = Class.forName(className);

            if (this.beanCacheMap.containsKey(className)) {
                instance = beanCacheMap.get(className);
            } else {
                instance = aClass.newInstance();
                this.beanCacheMap.put(className, instance);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    public int getBeanDefinitionCount() {
        return beanDefinitionMap.size();
    }


    public String[] getBeanDefinitionNames() {
        return beanDefinitionMap.keySet().toArray(new String[beanDefinitionMap.size()]);
    }

    public Properties getConfig() {
        return reader.getConfig();
    }
}
